mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Strict nulls check contrib/queryplan (#11917)
* remove angular query pla neditor and replace with non-angular version; strict null check contrib/queryPlan * fix compile
This commit is contained in:
@@ -300,8 +300,8 @@ export class QueryResultsView extends Disposable {
|
|||||||
this.dynamicModelViewTabs.forEach(t => t.clear());
|
this.dynamicModelViewTabs.forEach(t => t.clear());
|
||||||
|
|
||||||
this.resultsTab.view.state = this.input.state.gridPanelState;
|
this.resultsTab.view.state = this.input.state.gridPanelState;
|
||||||
this.qpTab.view.state = this.input.state.queryPlanState;
|
this.qpTab.view.setState(this.input.state.queryPlanState);
|
||||||
this.topOperationsTab.view.state = this.input.state.topOperationsState;
|
this.topOperationsTab.view.setState(this.input.state.topOperationsState);
|
||||||
this.chartTab.view.state = this.input.state.chartState;
|
this.chartTab.view.state = this.input.state.chartState;
|
||||||
this.dynamicModelViewTabs.forEach((dynamicTab: QueryModelViewTab) => {
|
this.dynamicModelViewTabs.forEach((dynamicTab: QueryModelViewTab) => {
|
||||||
dynamicTab.captureState(this.input.state.dynamicModelViewTabsState);
|
dynamicTab.captureState(this.input.state.dynamicModelViewTabsState);
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
class RunTimeInformation {
|
class RunTimeInformation {
|
||||||
runtimePerThreads: RuntimePerThread[];
|
constructor(public runtimePerThreads?: RuntimePerThread[]) { }
|
||||||
|
|
||||||
public get actualRows(): number {
|
public get actualRows(): number {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
if (this.runtimePerThreads) {
|
if (this.runtimePerThreads) {
|
||||||
this.runtimePerThreads.forEach(element => {
|
this.runtimePerThreads.forEach(element => {
|
||||||
total += element.actualRow;
|
total += element.actualRow ?? 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,26 +21,26 @@ class RunTimeInformation {
|
|||||||
let total = 0;
|
let total = 0;
|
||||||
if (this.runtimePerThreads) {
|
if (this.runtimePerThreads) {
|
||||||
this.runtimePerThreads.forEach(element => {
|
this.runtimePerThreads.forEach(element => {
|
||||||
total += element.actualExecutions;
|
total += element.actualExecutions ?? 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RuntimePerThread {
|
interface RuntimePerThread {
|
||||||
threadId: number;
|
threadId?: number;
|
||||||
actualRow: number;
|
actualRow?: number;
|
||||||
actualExecutionMode: string;
|
actualExecutionMode?: string;
|
||||||
actualExecutions: number;
|
actualExecutions?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IndexObject {
|
class IndexObject {
|
||||||
database: string;
|
public database?: string;
|
||||||
schema: string;
|
public schema?: string;
|
||||||
table: string;
|
public table?: string;
|
||||||
index: string;
|
public index?: string;
|
||||||
indexKind: string;
|
public indexKind?: string;
|
||||||
|
|
||||||
public get title() {
|
public get title() {
|
||||||
let title: string = '';
|
let title: string = '';
|
||||||
@@ -54,22 +55,23 @@ class IndexObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PlanNode {
|
class PlanNode {
|
||||||
root: PlanNode;
|
private childrenNodes: PlanNode[] = [];
|
||||||
subtreeCost: number;
|
|
||||||
private childrenNodes: PlanNode[];
|
public root?: PlanNode;
|
||||||
parent: PlanNode;
|
public subtreeCost?: number;
|
||||||
physicalOp: string;
|
public parent?: PlanNode;
|
||||||
logicalOp: string;
|
public physicalOp?: string;
|
||||||
id: number;
|
public logicalOp?: string;
|
||||||
estimateRows: string;
|
public id?: number;
|
||||||
estimateIo: string;
|
public estimateRows?: string;
|
||||||
estimateCpu: string;
|
public estimateIo?: string;
|
||||||
parallel: boolean;
|
public estimateCpu?: string;
|
||||||
partitioned: boolean;
|
public parallel?: boolean;
|
||||||
estimateRewinds: string;
|
public partitioned?: boolean;
|
||||||
estimateRebinds: string;
|
public estimateRewinds?: string;
|
||||||
runtimeInfo: RunTimeInformation;
|
public estimateRebinds?: string;
|
||||||
indexObject: IndexObject;
|
public runtimeInfo?: RunTimeInformation;
|
||||||
|
public indexObject?: IndexObject;
|
||||||
|
|
||||||
public addChildren(children: PlanNode[]): void {
|
public addChildren(children: PlanNode[]): void {
|
||||||
if (children) {
|
if (children) {
|
||||||
@@ -81,10 +83,10 @@ class PlanNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get totalSubTreeCost(): number {
|
public get totalSubTreeCost(): number {
|
||||||
let total = this.subtreeCost;
|
let total = this.subtreeCost ?? 0;
|
||||||
if (total === 0) {
|
if (total === 0) {
|
||||||
this.children.forEach(element => {
|
this.children.forEach(element => {
|
||||||
total += element.subtreeCost;
|
total += element.subtreeCost ?? 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
@@ -95,17 +97,17 @@ class PlanNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get cost(): number {
|
public get cost(): number {
|
||||||
let total = this.subtreeCost;
|
let total = this.subtreeCost ?? 0;
|
||||||
if (this.children && total !== 0) {
|
if (this.children && total !== 0) {
|
||||||
this.children.forEach(element => {
|
this.children.forEach(element => {
|
||||||
total -= element.subtreeCost;
|
total -= element.subtreeCost ?? 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get relativeCost(): number {
|
public get relativeCost(): number {
|
||||||
let overallCost = this.root.totalSubTreeCost;
|
let overallCost = this.root?.totalSubTreeCost ?? 0;
|
||||||
return overallCost > 0 ? this.cost / overallCost : 0;
|
return overallCost > 0 ? this.cost / overallCost : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +127,7 @@ class PlanNode {
|
|||||||
|
|
||||||
public get title(): string {
|
public get title(): string {
|
||||||
if (this.physicalOp === this.logicalOp) {
|
if (this.physicalOp === this.logicalOp) {
|
||||||
return this.physicalOp;
|
return this.physicalOp ?? '';
|
||||||
} else {
|
} else {
|
||||||
return `${this.physicalOp}(${this.logicalOp})`;
|
return `${this.physicalOp}(${this.logicalOp})`;
|
||||||
}
|
}
|
||||||
@@ -141,10 +143,10 @@ class PlanNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PlanXmlParser {
|
export class PlanXmlParser {
|
||||||
parser: DOMParser = new DOMParser();
|
public parser: DOMParser = new DOMParser();
|
||||||
doc: Document;
|
public doc?: Document;
|
||||||
planXml: string;
|
public planXml?: string;
|
||||||
root: PlanNode;
|
public root?: PlanNode;
|
||||||
|
|
||||||
constructor(planXml: string) {
|
constructor(planXml: string) {
|
||||||
|
|
||||||
@@ -179,7 +181,9 @@ export class PlanXmlParser {
|
|||||||
|
|
||||||
public get toTreeViewList(): PlanNode[] {
|
public get toTreeViewList(): PlanNode[] {
|
||||||
let operations: PlanNode[] = [];
|
let operations: PlanNode[] = [];
|
||||||
|
if (this.root) {
|
||||||
operations = this.addOperationsToList(operations, this.root.children);
|
operations = this.addOperationsToList(operations, this.root.children);
|
||||||
|
}
|
||||||
|
|
||||||
return operations;
|
return operations;
|
||||||
}
|
}
|
||||||
@@ -192,7 +196,7 @@ export class PlanXmlParser {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private findChildren(element: Element, elementName: string, untilNode: string = undefined): Element[] {
|
private findChildren(element: Element, elementName: string, untilNode: string | undefined = undefined): Element[] | undefined {
|
||||||
let elements: Element[] = [];
|
let elements: Element[] = [];
|
||||||
if (element === undefined) {
|
if (element === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -251,10 +255,10 @@ export class PlanXmlParser {
|
|||||||
|
|
||||||
private convertToPlanNode(element: Element): PlanNode {
|
private convertToPlanNode(element: Element): PlanNode {
|
||||||
let planNode = new PlanNode();
|
let planNode = new PlanNode();
|
||||||
planNode.id = this.findAttribute(element.attributes, 'NodeId');
|
planNode.id = Number(this.findAttribute(element.attributes, 'NodeId'));
|
||||||
planNode.logicalOp = this.findAttribute(element.attributes, 'LogicalOp');
|
planNode.logicalOp = this.findAttribute(element.attributes, 'LogicalOp');
|
||||||
planNode.physicalOp = this.findAttribute(element.attributes, 'PhysicalOp');
|
planNode.physicalOp = this.findAttribute(element.attributes, 'PhysicalOp');
|
||||||
planNode.subtreeCost = +this.findAttribute(element.attributes, 'EstimatedTotalSubtreeCost');
|
planNode.subtreeCost = Number(this.findAttribute(element.attributes, 'EstimatedTotalSubtreeCost'));
|
||||||
planNode.estimateRows = this.findAttribute(element.attributes, 'EstimateRows');
|
planNode.estimateRows = this.findAttribute(element.attributes, 'EstimateRows');
|
||||||
planNode.estimateCpu = this.findAttribute(element.attributes, 'EstimateCPU');
|
planNode.estimateCpu = this.findAttribute(element.attributes, 'EstimateCPU');
|
||||||
planNode.estimateIo = this.findAttribute(element.attributes, 'EstimateIO');
|
planNode.estimateIo = this.findAttribute(element.attributes, 'EstimateIO');
|
||||||
@@ -266,12 +270,12 @@ export class PlanXmlParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private convertToRuntimeInfo(element: Element): RuntimePerThread {
|
private convertToRuntimeInfo(element: Element): RuntimePerThread {
|
||||||
let runtimeNode = new RuntimePerThread();
|
return {
|
||||||
runtimeNode.actualExecutionMode = this.findAttribute(element.attributes, 'ActualExecutionMode');
|
actualExecutionMode: this.findAttribute(element.attributes, 'ActualExecutionMode'),
|
||||||
runtimeNode.actualExecutions = +this.findAttribute(element.attributes, 'ActualExecutions');
|
actualExecutions: Number(this.findAttribute(element.attributes, 'ActualExecutions')),
|
||||||
runtimeNode.actualRow = +this.findAttribute(element.attributes, 'ActualRows');
|
actualRow: Number(this.findAttribute(element.attributes, 'ActualRows')),
|
||||||
runtimeNode.threadId = +this.findAttribute(element.attributes, 'Thread');
|
threadId: Number(this.findAttribute(element.attributes, 'Thread'))
|
||||||
return runtimeNode;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertToObject(element: Element): IndexObject {
|
private convertToObject(element: Element): IndexObject {
|
||||||
@@ -284,12 +288,13 @@ export class PlanXmlParser {
|
|||||||
return objectNode;
|
return objectNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private findAttribute(attributes: NamedNodeMap, attName: string): any {
|
private findAttribute(attributes: NamedNodeMap, attName: string): string | undefined {
|
||||||
for (let index = 0; index < attributes.length; index++) {
|
for (let index = 0; index < attributes.length; index++) {
|
||||||
let attribute = attributes[index];
|
let attribute = attributes[index];
|
||||||
if (attribute.name === attName) {
|
if (attribute.name === attName) {
|
||||||
return attribute.value;
|
return attribute.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!./media/qp';
|
|
||||||
|
|
||||||
import { ElementRef, Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|
||||||
import * as QP from 'html-query-plan';
|
|
||||||
|
|
||||||
import { IQueryPlanParams, IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
|
||||||
|
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
|
||||||
import { registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService';
|
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
|
||||||
|
|
||||||
export const QUERYPLAN_SELECTOR: string = 'queryplan-component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: QUERYPLAN_SELECTOR,
|
|
||||||
template: `
|
|
||||||
<div #container class="fullsize" style="overflow: scroll">
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
export class QueryPlanComponent implements OnDestroy, OnInit {
|
|
||||||
|
|
||||||
private _planXml: string;
|
|
||||||
private _disposables: Array<IDisposable> = [];
|
|
||||||
@ViewChild('container', { read: ElementRef }) _container: ElementRef;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Inject(IBootstrapParams) private _params: IQueryPlanParams
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
dispose(this._disposables);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (this._params) {
|
|
||||||
this.planXml = this._params.planXml;
|
|
||||||
}
|
|
||||||
this._disposables.push(registerThemingParticipant(this._updateTheme));
|
|
||||||
}
|
|
||||||
|
|
||||||
public set planXml(val: string) {
|
|
||||||
this._planXml = val;
|
|
||||||
if (this._planXml) {
|
|
||||||
QP.showPlan(this._container.nativeElement, this._planXml, {
|
|
||||||
jsTooltips: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateTheme(theme: IColorTheme, collector: ICssStyleCollector) {
|
|
||||||
let backgroundColor = theme.getColor(colors.editorBackground);
|
|
||||||
let foregroundColor = theme.getColor(colors.editorForeground);
|
|
||||||
|
|
||||||
if (backgroundColor) {
|
|
||||||
collector.addRule(`div.qp-node, .qp-tt, .qp-root { background-color: ${backgroundColor} }`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foregroundColor) {
|
|
||||||
collector.addRule(`.qp-root { color: ${foregroundColor} }`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver, Type } from '@angular/core';
|
|
||||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
import { providerIterator } from 'sql/workbench/services/bootstrap/browser/bootstrapService';
|
|
||||||
import { QueryPlanComponent } from 'sql/workbench/contrib/queryPlan/browser/queryPlan.component';
|
|
||||||
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { IBootstrapParams, ISelector } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
|
||||||
|
|
||||||
// Connection Dashboard main angular module
|
|
||||||
export const QueryPlanModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
QueryPlanComponent
|
|
||||||
],
|
|
||||||
entryComponents: [QueryPlanComponent],
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
BrowserModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
|
||||||
{ provide: IBootstrapParams, useValue: params },
|
|
||||||
{ provide: ISelector, useValue: selector },
|
|
||||||
...providerIterator(instantiationService)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
class ModuleClass {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
|
||||||
@Inject(ISelector) private selector: string
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngDoBootstrap(appRef: ApplicationRef) {
|
|
||||||
const factory = this._resolver.resolveComponentFactory(QueryPlanComponent);
|
|
||||||
(<any>factory).factory.selector = this.selector;
|
|
||||||
appRef.bootstrap(factory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ModuleClass;
|
|
||||||
};
|
|
||||||
@@ -31,10 +31,10 @@ export class QueryPlanTab implements IPanelTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class QueryPlanView implements IPanelView {
|
export class QueryPlanView implements IPanelView {
|
||||||
private qp: QueryPlan;
|
private qp?: QueryPlan;
|
||||||
private xml: string;
|
private xml?: string;
|
||||||
private container = document.createElement('div');
|
private container = document.createElement('div');
|
||||||
private _state: QueryPlanState;
|
private _state?: QueryPlanState;
|
||||||
|
|
||||||
public render(container: HTMLElement): void {
|
public render(container: HTMLElement): void {
|
||||||
container.appendChild(this.container);
|
container.appendChild(this.container);
|
||||||
@@ -50,7 +50,6 @@ export class QueryPlanView implements IPanelView {
|
|||||||
dispose() {
|
dispose() {
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
this.qp = undefined;
|
this.qp = undefined;
|
||||||
this.container = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public layout(dimension: Dimension): void {
|
public layout(dimension: Dimension): void {
|
||||||
@@ -79,38 +78,38 @@ export class QueryPlanView implements IPanelView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public set state(val: QueryPlanState) {
|
public setState(val: QueryPlanState) {
|
||||||
this._state = val;
|
this._state = val;
|
||||||
if (this.state.xml) {
|
if (this._state.xml) {
|
||||||
this.showPlan(this.state.xml);
|
this.showPlan(this._state.xml);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get state(): QueryPlanState {
|
public get state(): QueryPlanState | undefined {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryPlan {
|
export class QueryPlan {
|
||||||
private _xml: string;
|
private _xml?: string;
|
||||||
constructor(private container: HTMLElement) {
|
constructor(private container: HTMLElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public set xml(xml: string) {
|
public set xml(xml: string | undefined) {
|
||||||
this._xml = xml;
|
this._xml = xml;
|
||||||
clearNode(this.container);
|
clearNode(this.container);
|
||||||
if (this.xml) {
|
if (this.xml) {
|
||||||
QP.showPlan(this.container, this._xml, {
|
QP.showPlan(this.container, this.xml, {
|
||||||
jsTooltips: false
|
jsTooltips: false
|
||||||
});
|
});
|
||||||
(<any>this.container.querySelectorAll('div.qp-tt')).forEach(toolTip => {
|
this.container.querySelectorAll('div.qp-tt').forEach(toolTip => {
|
||||||
toolTip.classList.add('monaco-editor');
|
toolTip.classList.add('monaco-editor');
|
||||||
toolTip.classList.add('monaco-editor-hover');
|
toolTip.classList.add('monaco-editor-hover');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get xml(): string {
|
public get xml(): string | undefined {
|
||||||
return this._xml;
|
return this._xml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,23 +8,20 @@ import { EditorOptions } from 'vs/workbench/common/editor';
|
|||||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
|
import { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
|
||||||
import { QueryPlanModule } from 'sql/workbench/contrib/queryPlan/browser/queryPlan.module';
|
|
||||||
import { bootstrapAngular } from 'sql/workbench/services/bootstrap/browser/bootstrapService';
|
|
||||||
import { IQueryPlanParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
|
||||||
import { QUERYPLAN_SELECTOR } from 'sql/workbench/contrib/queryPlan/browser/queryPlan.component';
|
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
|
import { QueryPlanView } from 'sql/workbench/contrib/queryPlan/browser/queryPlan';
|
||||||
|
|
||||||
export class QueryPlanEditor extends BaseEditor {
|
export class QueryPlanEditor extends BaseEditor {
|
||||||
|
|
||||||
public static ID: string = 'workbench.editor.queryplan';
|
public static ID: string = 'workbench.editor.queryplan';
|
||||||
|
|
||||||
|
private view = this._register(new QueryPlanView());
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ITelemetryService telemetryService: ITelemetryService,
|
@ITelemetryService telemetryService: ITelemetryService,
|
||||||
@IThemeService themeService: IThemeService,
|
@IThemeService themeService: IThemeService,
|
||||||
@IInstantiationService private instantiationService: IInstantiationService,
|
|
||||||
@IStorageService storageService: IStorageService
|
@IStorageService storageService: IStorageService
|
||||||
) {
|
) {
|
||||||
super(QueryPlanEditor.ID, telemetryService, themeService, storageService);
|
super(QueryPlanEditor.ID, telemetryService, themeService, storageService);
|
||||||
@@ -45,12 +42,14 @@ export class QueryPlanEditor extends BaseEditor {
|
|||||||
//(plus it's probable that we won't be using this control in the future anyways if development)
|
//(plus it's probable that we won't be using this control in the future anyways if development)
|
||||||
//continues on the Query plan feature
|
//continues on the Query plan feature
|
||||||
parent.style.background = '#fff';
|
parent.style.background = '#fff';
|
||||||
|
this.view.render(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets focus on this editor. Specifically, it sets the focus on the hosted text editor.
|
* Sets focus on this editor. Specifically, it sets the focus on the hosted text editor.
|
||||||
*/
|
*/
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
|
this.view.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,6 +57,7 @@ export class QueryPlanEditor extends BaseEditor {
|
|||||||
* To be called when the container of this editor changes size.
|
* To be called when the container of this editor changes size.
|
||||||
*/
|
*/
|
||||||
public layout(dimension: DOM.Dimension): void {
|
public layout(dimension: DOM.Dimension): void {
|
||||||
|
this.view.layout(dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setInput(input: QueryPlanInput, options: EditorOptions): Promise<void> {
|
public async setInput(input: QueryPlanInput, options: EditorOptions): Promise<void> {
|
||||||
@@ -65,49 +65,10 @@ export class QueryPlanEditor extends BaseEditor {
|
|||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
await input.resolve();
|
await input.resolve();
|
||||||
if (!input.hasInitialized) {
|
|
||||||
this.bootstrapAngular(input);
|
|
||||||
}
|
|
||||||
this.revealElementWithTagName(input.uniqueSelector, this.getContainer());
|
|
||||||
|
|
||||||
return super.setInput(input, options, CancellationToken.None);
|
await super.setInput(input, options, CancellationToken.None);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
this.view.showPlan(input.planXml!);
|
||||||
* Reveal the child element with the given tagName and hide all other elements.
|
|
||||||
*/
|
|
||||||
private revealElementWithTagName(tagName: string, parent: HTMLElement): void {
|
|
||||||
let elementToReveal: HTMLElement;
|
|
||||||
|
|
||||||
for (let i = 0; i < parent.children.length; i++) {
|
|
||||||
let child: HTMLElement = <HTMLElement>parent.children[i];
|
|
||||||
if (child.tagName && child.tagName.toLowerCase() === tagName && !elementToReveal) {
|
|
||||||
elementToReveal = child;
|
|
||||||
} else {
|
|
||||||
child.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elementToReveal) {
|
|
||||||
elementToReveal.style.display = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the angular components and record for this input that we have done so
|
|
||||||
*/
|
|
||||||
private bootstrapAngular(input: QueryPlanInput): void {
|
|
||||||
// Get the bootstrap params and perform the bootstrap
|
|
||||||
let params: IQueryPlanParams = {
|
|
||||||
planXml: input.planXml
|
|
||||||
};
|
|
||||||
|
|
||||||
let uniqueSelector = this.instantiationService.invokeFunction(bootstrapAngular,
|
|
||||||
QueryPlanModule,
|
|
||||||
this.getContainer(),
|
|
||||||
QUERYPLAN_SELECTOR,
|
|
||||||
params);
|
|
||||||
input.setUniqueSelector(uniqueSelector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export class TopOperationsTab extends Disposable implements IPanelTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TopOperationsView extends Disposable implements IPanelView {
|
export class TopOperationsView extends Disposable implements IPanelView {
|
||||||
private _state: TopOperationsState;
|
private _state?: TopOperationsState;
|
||||||
private table: Table<any>;
|
private table: Table<any>;
|
||||||
private container = document.createElement('div');
|
private container = document.createElement('div');
|
||||||
private dataView = new TableDataView();
|
private dataView = new TableDataView();
|
||||||
@@ -86,19 +86,19 @@ export class TopOperationsView extends Disposable implements IPanelView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public showPlan(xml: string) {
|
public showPlan(xml: string) {
|
||||||
this.state.xml = xml;
|
this.state!.xml = xml;
|
||||||
this.dataView.clear();
|
this.dataView.clear();
|
||||||
let parser = new PlanXmlParser(xml);
|
let parser = new PlanXmlParser(xml);
|
||||||
let operations = parser.topOperations;
|
let operations = parser.topOperations;
|
||||||
let data = operations.map(i => {
|
let data = operations.map(i => {
|
||||||
return {
|
return {
|
||||||
operation: i.title,
|
operation: i.title,
|
||||||
object: i.indexObject.title,
|
object: i.indexObject?.title,
|
||||||
estCost: i.estimatedOperatorCost,
|
estCost: i.estimatedOperatorCost,
|
||||||
estSubtreeCost: i.subtreeCost,
|
estSubtreeCost: i.subtreeCost,
|
||||||
actualRows: i.runtimeInfo.actualRows,
|
actualRows: i.runtimeInfo?.actualRows,
|
||||||
estRows: i.estimateRows,
|
estRows: i.estimateRows,
|
||||||
actualExecutions: i.runtimeInfo.actualExecutions,
|
actualExecutions: i.runtimeInfo?.actualExecutions,
|
||||||
estCPUCost: i.estimateCpu,
|
estCPUCost: i.estimateCpu,
|
||||||
estIOCost: i.estimateIo,
|
estIOCost: i.estimateIo,
|
||||||
parallel: i.parallel,
|
parallel: i.parallel,
|
||||||
@@ -112,14 +112,14 @@ export class TopOperationsView extends Disposable implements IPanelView {
|
|||||||
this.dataView.push(data);
|
this.dataView.push(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set state(val: TopOperationsState) {
|
public setState(val: TopOperationsState) {
|
||||||
this._state = val;
|
this._state = val;
|
||||||
if (this.state.xml) {
|
if (this._state.xml) {
|
||||||
this.showPlan(this.state.xml);
|
this.showPlan(this._state.xml);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get state(): TopOperationsState {
|
public get state(): TopOperationsState | undefined {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,30 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/editor';
|
import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/editor';
|
||||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
|
||||||
import { IFileService } from 'vs/platform/files/common/files';
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ILanguageAssociation } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
|
import { ILanguageAssociation } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
|
||||||
|
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
|
|
||||||
export class QueryPlanConverter implements ILanguageAssociation {
|
export class QueryPlanConverter implements ILanguageAssociation {
|
||||||
static readonly languages = ['sqlplan'];
|
static readonly languages = ['sqlplan'];
|
||||||
|
|
||||||
constructor(@IInstantiationService private instantiationService: IInstantiationService) { }
|
constructor(
|
||||||
|
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||||
|
@IEditorService private readonly editorService: IEditorService
|
||||||
|
) { }
|
||||||
|
|
||||||
convertInput(activeEditor: IEditorInput): QueryPlanInput {
|
convertInput(activeEditor: IEditorInput): QueryPlanInput | undefined {
|
||||||
|
if (activeEditor.resource) {
|
||||||
return this.instantiationService.createInstance(QueryPlanInput, activeEditor.resource);
|
return this.instantiationService.createInstance(QueryPlanInput, activeEditor.resource);
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
createBase(activeEditor: QueryPlanInput): IEditorInput {
|
createBase(activeEditor: QueryPlanInput): IEditorInput {
|
||||||
return undefined;
|
return this.editorService.createEditorInput({ resource: activeEditor.resource });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +36,7 @@ export class QueryPlanInput extends EditorInput {
|
|||||||
public static ID: string = 'workbench.editorinputs.queryplan';
|
public static ID: string = 'workbench.editorinputs.queryplan';
|
||||||
public static SCHEMA: string = 'queryplan';
|
public static SCHEMA: string = 'queryplan';
|
||||||
|
|
||||||
private _uniqueSelector: string;
|
private _xml?: string;
|
||||||
private _xml: string;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _uri: URI,
|
private _uri: URI,
|
||||||
@@ -40,10 +45,6 @@ export class QueryPlanInput extends EditorInput {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setUniqueSelector(uniqueSelector: string): void {
|
|
||||||
this._uniqueSelector = uniqueSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTypeId(): string {
|
public getTypeId(): string {
|
||||||
return UntitledTextEditorInput.ID;
|
return UntitledTextEditorInput.ID;
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,7 @@ export class QueryPlanInput extends EditorInput {
|
|||||||
return 'Query Plan';
|
return 'Query Plan';
|
||||||
}
|
}
|
||||||
|
|
||||||
public get planXml(): string {
|
public get planXml(): string | undefined {
|
||||||
return this._xml;
|
return this._xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,24 +65,11 @@ export class QueryPlanInput extends EditorInput {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionProfile(): IConnectionProfile {
|
public async resolve(refresh?: boolean): Promise<EditorModel | null> {
|
||||||
//return this._connection.connectionProfile;
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async resolve(refresh?: boolean): Promise<EditorModel> {
|
|
||||||
if (!this._xml) {
|
if (!this._xml) {
|
||||||
this._xml = (await this.fileService.readFile(this._uri)).value.toString();
|
this._xml = (await this.fileService.readFile(this._uri)).value.toString();
|
||||||
}
|
}
|
||||||
return undefined;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
public get hasInitialized(): boolean {
|
|
||||||
return !!this._uniqueSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get uniqueSelector(): string {
|
|
||||||
return this._uniqueSelector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get resource(): URI | undefined {
|
get resource(): URI | undefined {
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
"./sql/workbench/contrib/profiler/**/*.ts", // 204 errors
|
"./sql/workbench/contrib/profiler/**/*.ts", // 204 errors
|
||||||
"./sql/workbench/contrib/query/**/*.ts", // 3342 errors
|
"./sql/workbench/contrib/query/**/*.ts", // 3342 errors
|
||||||
"./sql/workbench/contrib/queryHistory/**/*.ts", // 432 errors
|
"./sql/workbench/contrib/queryHistory/**/*.ts", // 432 errors
|
||||||
"./sql/workbench/contrib/queryPlan/**/*.ts", // 52 errors
|
|
||||||
"./sql/workbench/contrib/restore/**/*.ts", // 142 errors
|
"./sql/workbench/contrib/restore/**/*.ts", // 142 errors
|
||||||
"./sql/workbench/contrib/scripting/**/*.ts", // 280 errors
|
"./sql/workbench/contrib/scripting/**/*.ts", // 280 errors
|
||||||
"./sql/workbench/contrib/tasks/**/*.ts", // 100 errors
|
"./sql/workbench/contrib/tasks/**/*.ts", // 100 errors
|
||||||
|
|||||||
Reference in New Issue
Block a user