Deleting old query plan code (#19996)

This commit is contained in:
Aasim Khan
2022-07-11 19:14:47 -07:00
committed by GitHub
parent 29d0acf180
commit 53a0388858
9 changed files with 0 additions and 952 deletions

View File

@@ -1,224 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
div.qp-node {
margin: 2px;
padding: 2px;
border: 1px solid;
}
div.qp-statement-header {
margin: 2px;
padding: 2px;
font-size: 12px;
line-height: normal;
}
div.qp-node,
div.qp-tt {
font-size: 11px;
line-height: normal;
}
.qp-node>div {
font-family: Monospace;
text-align: center;
}
div[class|='qp-icon'] {
height: 32px;
width: 32px;
margin-left: auto;
margin-right: auto;
background-repeat: no-repeat;
}
.qp-tt {
top: 4em;
left: 2em;
border: 1px solid;
padding: 2px;
width: 30em;
}
.qp-tt div,
.qp-tt table {
font-family: Sans-Serif;
text-align: left;
}
.qp-tt table {
border-width: 0px;
border-spacing: 0px;
margin-top: 10px;
margin-bottom: 10px;
width: 100%;
}
.qp-tt td,
.qp-tt th {
font-size: 11px;
border-bottom: solid 1px;
padding: 1px;
}
.qp-tt td {
text-align: right;
padding-left: 10px;
}
.qp-tt th {
text-align: left;
}
.qp-bold,
.qp-tt-header {
font-weight: bold;
}
.qp-tt-header {
text-align: center;
}
/* Icons */
.qp-icon-Catchall{background: url('qp_icons.png') -96px -0px }
.qp-icon-ArithmeticExpression{background: url('qp_icons.png') -0px -0px }
.qp-icon-Assert{background: url('qp_icons.png') -32px -0px }
.qp-icon-Assign{background: url('qp_icons.png') -64px -0px }
.qp-icon-Bitmap{background: url('qp_icons.png') -96px -0px }
.qp-icon-BookmarkLookup{background: url('qp_icons.png') -128px -0px }
.qp-icon-ClusteredIndexDelete{background: url('qp_icons.png') -160px -0px }
.qp-icon-ClusteredIndexInsert{background: url('qp_icons.png') -192px -0px }
.qp-icon-ClusteredIndexScan{background: url('qp_icons.png') -224px -0px }
.qp-icon-ClusteredIndexSeek{background: url('qp_icons.png') -256px -0px }
.qp-icon-KeyLookup{background: url('qp_icons.png') -256px -0px }
.qp-icon-ClusteredIndexUpdate{background: url('qp_icons.png') -288px -0px }
.qp-icon-Collapse{background: url('qp_icons.png') -0px -32px }
.qp-icon-ComputeScalar{background: url('qp_icons.png') -32px -32px }
.qp-icon-Concatenation{background: url('qp_icons.png') -64px -32px }
.qp-icon-ConstantScan{background: url('qp_icons.png') -96px -32px }
.qp-icon-Convert{background: url('qp_icons.png') -128px -32px }
.qp-icon-CursorCatchall{background: url('qp_icons.png') -96px -0px }
.qp-icon-Declare{background: url('qp_icons.png') -160px -32px }
.qp-icon-Delete{background: url('qp_icons.png') -288px -160px }
.qp-icon-DistributeStreams{background: url('qp_icons.png') -224px -32px }
.qp-icon-Dynamic{background: url('qp_icons.png') -256px -32px }
.qp-icon-EagerSpool{background: url('qp_icons.png') -192px -160px }
.qp-icon-FetchQuery{background: url('qp_icons.png') -288px -32px }
.qp-icon-Filter{background: url('qp_icons.png') -0px -64px }
.qp-icon-GatherStreams{background: url('qp_icons.png') -32px -64px }
.qp-icon-HashMatch{background: url('qp_icons.png') -64px -64px }
.qp-icon-HashMatchRoot{background: url('qp_icons.png') -64px -64px }
.qp-icon-HashMatchTeam{background: url('qp_icons.png') -64px -64px }
.qp-icon-If{background: url('qp_icons.png') -96px -64px }
.qp-icon-Insert{background: url('qp_icons.png') -0px -192px }
.qp-icon-InsertedScan{background: url('qp_icons.png') -128px -64px }
.qp-icon-Intrinsic{background: url('qp_icons.png') -160px -64px }
.qp-icon-IteratorCatchall{background: url('qp_icons.png') -96px -0px }
.qp-icon-Keyset{background: url('qp_icons.png') -192px -64px }
.qp-icon-LanguageElementCatchall{background: url('qp_icons.png') -96px -0px }
.qp-icon-LazySpool{background: url('qp_icons.png') -192px -160px }
.qp-icon-LogRowScan{background: url('qp_icons.png') -224px -64px }
.qp-icon-MergeInterval{background: url('qp_icons.png') -256px -64px }
.qp-icon-MergeJoin{background: url('qp_icons.png') -288px -64px }
.qp-icon-NestedLoops{background: url('qp_icons.png') -0px -96px }
.qp-icon-NonclusteredIndexDelete{background: url('qp_icons.png') -32px -96px }
.qp-icon-NonclusteredIndexInsert{background: url('qp_icons.png') -64px -96px }
.qp-icon-IndexScan{background: url('qp_icons.png') -96px -96px }
.qp-icon-IndexSeek{background: url('qp_icons.png') -128px -96px }
.qp-icon-NonclusteredIndexSpool{background: url('qp_icons.png') -160px -96px }
.qp-icon-NonclusteredIndexUpdate{background: url('qp_icons.png') -192px -96px }
.qp-icon-OnlineIndexInsert{background: url('qp_icons.png') -224px -96px }
.qp-icon-ParameterTableScan{background: url('qp_icons.png') -256px -96px }
.qp-icon-PopulationQuery{background: url('qp_icons.png') -288px -96px }
.qp-icon-RdiLookup{background: url('qp_icons.png') -0px -128px }
.qp-icon-RefreshQuery{background: url('qp_icons.png') -32px -128px }
.qp-icon-RemoteDelete{background: url('qp_icons.png') -64px -128px }
.qp-icon-RemoteInsert{background: url('qp_icons.png') -96px -128px }
.qp-icon-RemoteQuery{background: url('qp_icons.png') -128px -128px }
.qp-icon-RemoteScan{background: url('qp_icons.png') -160px -128px }
.qp-icon-RemoteUpdate{background: url('qp_icons.png') -192px -128px }
.qp-icon-RepartitionStreams{background: url('qp_icons.png') -224px -128px }
.qp-icon-Result{background: url('qp_icons.png') -256px -128px }
.qp-icon-RowCountSpool{background: url('qp_icons.png') -288px -128px }
.qp-icon-Segment{background: url('qp_icons.png') -0px -160px }
.qp-icon-Sequence{background: url('qp_icons.png') -32px -160px }
.qp-icon-Sequenceproject{background: url('qp_icons.png') -64px -160px }
.qp-icon-Snapshot{background: url('qp_icons.png') -96px -160px }
.qp-icon-Sort{background: url('qp_icons.png') -128px -160px }
.qp-icon-Split{background: url('qp_icons.png') -160px -160px }
.qp-icon-Spool{background: url('qp_icons.png') -192px -160px }
.qp-icon-Statement{background: url('qp_icons.png') -256px -128px }
.qp-icon-StreamAggregate{background: url('qp_icons.png') -224px -160px }
.qp-icon-Switch{background: url('qp_icons.png') -256px -160px }
.qp-icon-TableDelete{background: url('qp_icons.png') -288px -160px }
.qp-icon-TableInsert{background: url('qp_icons.png') -0px -192px }
.qp-icon-TableScan{background: url('qp_icons.png') -32px -192px }
.qp-icon-TableSpool{background: url('qp_icons.png') -64px -192px }
.qp-icon-TableUpdate{background: url('qp_icons.png') -96px -192px }
.qp-icon-TableValuedFunction{background: url('qp_icons.png') -128px -192px }
.qp-icon-Top{background: url('qp_icons.png') -160px -192px }
.qp-icon-Udx{background: url('qp_icons.png') -192px -192px }
.qp-icon-Update{background: url('qp_icons.png') -96px -192px }
.qp-icon-While{background: url('qp_icons.png') -224px -192px }
.qp-icon-PopulateQuery{background: url('qp_icons.png') -96px -0px }
.qp-icon-StmtCursor{background: url('qp_icons.png') -96px -0px }
.qp-icon-SequenceProject{background: url('qp_icons.png') -96px -0px }
.qp-icon-FastForward{background: url('qp_icons.png') -96px -0px }
.qp-icon-SnapShot{background: url('qp_icons.png') -96px -0px }
/* Layout - can't touch this */
.qp-tt {
position: absolute;
z-index: 1;
white-space: normal;
-webkit-transition-delay: 0.5s;
transition-delay: 0.5s;
}
div.qp-node .qp-tt,
.qp-noCssTooltip div.qp-node:hover .qp-tt {
visibility: hidden;
position: absolute;
overflow: hidden;
}
div.qp-node:hover .qp-tt {
visibility: visible;
}
.qp-tt table {
white-space: nowrap;
}
.qp-node {
position: relative;
white-space: nowrap;
}
.qp-tr {
display: table;
}
.qp-tr>div {
display: table-cell;
padding-left: 15px;
}
.qp-root {
display: table;
position: relative;
}
.qp-root svg {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 0;
background: transparent;
pointer-events: none;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -1,300 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
class RunTimeInformation {
constructor(public runtimePerThreads?: RuntimePerThread[]) { }
public get actualRows(): number {
let total = 0;
if (this.runtimePerThreads) {
this.runtimePerThreads.forEach(element => {
total += element.actualRow ?? 0;
});
}
return total;
}
public get actualExecutions(): number {
let total = 0;
if (this.runtimePerThreads) {
this.runtimePerThreads.forEach(element => {
total += element.actualExecutions ?? 0;
});
}
return total;
}
}
interface RuntimePerThread {
threadId?: number;
actualRow?: number;
actualExecutionMode?: string;
actualExecutions?: number;
}
class IndexObject {
public database?: string;
public schema?: string;
public table?: string;
public index?: string;
public indexKind?: string;
public get title() {
let title: string = '';
if (this.database && this.schema && this.table) {
title = `${this.database}.${this.schema}.${this.table}.${this.index}`;
if (this.indexKind && this.indexKind !== '') {
title += `(${this.indexKind})`;
}
}
return title;
}
}
class PlanNode {
private childrenNodes: PlanNode[] = [];
public root?: PlanNode;
public subtreeCost?: number;
public parent?: PlanNode;
public physicalOp?: string;
public logicalOp?: string;
public id?: number;
public estimateRows?: string;
public estimateIo?: string;
public estimateCpu?: string;
public parallel?: boolean;
public partitioned?: boolean;
public estimateRewinds?: string;
public estimateRebinds?: string;
public runtimeInfo?: RunTimeInformation;
public indexObject?: IndexObject;
public addChildren(children: PlanNode[]): void {
if (children) {
children.forEach(element => {
element.parent = this;
});
}
this.childrenNodes = children;
}
public get totalSubTreeCost(): number {
let total = this.subtreeCost ?? 0;
if (total === 0) {
this.children.forEach(element => {
total += element.subtreeCost ?? 0;
});
}
return total;
}
public get children(): PlanNode[] {
return this.childrenNodes;
}
public get cost(): number {
let total = this.subtreeCost ?? 0;
if (this.children && total !== 0) {
this.children.forEach(element => {
total -= element.subtreeCost ?? 0;
});
}
return total;
}
public get relativeCost(): number {
let overallCost = this.root?.totalSubTreeCost ?? 0;
return overallCost > 0 ? this.cost / overallCost : 0;
}
public get estimatedOperatorCost(): number {
return Math.round(this.relativeCost * 100);
}
public get estimatedSubtreeCost(): number {
let total = this.estimatedOperatorCost;
if (this.children) {
this.children.forEach(element => {
total += element.estimatedSubtreeCost;
});
}
return total;
}
public get title(): string {
if (this.physicalOp === this.logicalOp) {
return this.physicalOp ?? '';
} else {
return `${this.physicalOp}(${this.logicalOp})`;
}
}
public get treeViewPrefix(): string {
return this.parent === undefined ? '' : `${this.parent.treeViewPrefix}-----`;
}
public get treeViewTitle(): string {
return `${this.treeViewPrefix}${this.title}`;
}
}
export class PlanXmlParser {
public parser: DOMParser = new DOMParser();
public doc?: Document;
public planXml?: string;
public root?: PlanNode;
constructor(planXml: string) {
this.doc = this.parser.parseFromString(planXml, 'application/xml');
this.planXml = planXml;
let queryPlanNode = this.findChildren(this.doc.children[0], 'QueryPlan');
if (queryPlanNode && queryPlanNode.length > 0) {
this.root = new PlanNode();
let ops = this.createPlanNodes(queryPlanNode[0], 'RelOp', this.root);
this.root.addChildren(ops);
this.root.subtreeCost = 0;
}
}
public get topOperations(): PlanNode[] {
let operations: PlanNode[] = [];
if (this.root && this.root.children) {
operations = this.addOperationsToList(operations, this.root.children);
operations.sort((a, b) => {
if (a.estimatedOperatorCost > b.estimatedOperatorCost) {
return -1;
} else if (a.estimatedOperatorCost <= b.estimatedOperatorCost) {
return 1;
} else {
return 0;
}
});
}
return operations;
}
public get toTreeViewList(): PlanNode[] {
let operations: PlanNode[] = [];
if (this.root) {
operations = this.addOperationsToList(operations, this.root.children);
}
return operations;
}
private addOperationsToList(list: PlanNode[], nodes: PlanNode[]): PlanNode[] {
list = list.concat(nodes);
nodes.forEach(element => {
list = this.addOperationsToList(list, element.children);
});
return list;
}
private findChildren(element: Element, elementName: string, untilNode: string | undefined = undefined): Element[] | undefined {
let elements: Element[] = [];
if (element === undefined) {
return undefined;
}
for (let index = 0; index < element.childNodes.length; index++) {
if (element.childNodes[index].nodeName.toLocaleLowerCase() === elementName.toLocaleLowerCase()) {
elements = elements.concat(element.children[index]);
}
}
if (elements.length > 0) {
return elements;
}
for (let index = 0; index < element.childNodes.length; index++) {
if (untilNode && element.childNodes[index].nodeName === untilNode) {
continue;
}
let result = this.findChildren(element.children[index], elementName, untilNode);
if (result !== undefined) {
return result;
}
}
return undefined;
}
private createPlanNodes(element: Element, elementName: string, root: PlanNode): PlanNode[] {
let nodePlans: PlanNode[] = [];
let children = this.findChildren(element, elementName);
if (children) {
for (let index = 0; index < children.length; index++) {
let childNode = children[index];
let planNode = this.convertToPlanNode(childNode);
planNode.root = root;
planNode.addChildren(this.createPlanNodes(childNode, elementName, root));
planNode.runtimeInfo = new RunTimeInformation();
planNode.indexObject = new IndexObject();
let runtimeInfoNodes = this.findChildren(childNode, 'RunTimeCountersPerThread');
if (runtimeInfoNodes) {
planNode.runtimeInfo.runtimePerThreads = runtimeInfoNodes.map(x => this.convertToRuntimeInfo(x));
}
let objectNodes = this.findChildren(childNode, 'Object', 'RelOp');
if (objectNodes && objectNodes.length > 0) {
planNode.indexObject = this.convertToObject(objectNodes[0]);
}
nodePlans = nodePlans.concat(planNode);
}
}
return nodePlans;
}
private convertToPlanNode(element: Element): PlanNode {
let planNode = new PlanNode();
planNode.id = Number(this.findAttribute(element.attributes, 'NodeId'));
planNode.logicalOp = this.findAttribute(element.attributes, 'LogicalOp');
planNode.physicalOp = this.findAttribute(element.attributes, 'PhysicalOp');
planNode.subtreeCost = Number(this.findAttribute(element.attributes, 'EstimatedTotalSubtreeCost'));
planNode.estimateRows = this.findAttribute(element.attributes, 'EstimateRows');
planNode.estimateCpu = this.findAttribute(element.attributes, 'EstimateCPU');
planNode.estimateIo = this.findAttribute(element.attributes, 'EstimateIO');
planNode.estimateRebinds = this.findAttribute(element.attributes, 'EstimateRebinds');
planNode.estimateRewinds = this.findAttribute(element.attributes, 'EstimateRewinds');
planNode.parallel = this.findAttribute(element.attributes, 'Parallel') === '1';
planNode.partitioned = this.findAttribute(element.attributes, 'Partitioned') === '1';
return planNode;
}
private convertToRuntimeInfo(element: Element): RuntimePerThread {
return {
actualExecutionMode: this.findAttribute(element.attributes, 'ActualExecutionMode'),
actualExecutions: Number(this.findAttribute(element.attributes, 'ActualExecutions')),
actualRow: Number(this.findAttribute(element.attributes, 'ActualRows')),
threadId: Number(this.findAttribute(element.attributes, 'Thread'))
};
}
private convertToObject(element: Element): IndexObject {
let objectNode = new IndexObject();
objectNode.database = this.findAttribute(element.attributes, 'Database');
objectNode.index = this.findAttribute(element.attributes, 'Index');
objectNode.indexKind = this.findAttribute(element.attributes, 'IndexKind');
objectNode.schema = this.findAttribute(element.attributes, 'Schema');
objectNode.table = this.findAttribute(element.attributes, 'Table');
return objectNode;
}
private findAttribute(attributes: NamedNodeMap, attName: string): string | undefined {
for (let index = 0; index < attributes.length; index++) {
let attribute = attributes[index];
if (attribute.name === attName) {
return attribute.value;
}
}
return undefined;
}
}

View File

@@ -1,56 +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 { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor';
import { EditorExtensions } from 'vs/workbench/common/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Registry } from 'vs/platform/registry/common/platform';
import { QueryPlanEditor } from 'sql/workbench/contrib/queryPlan/browser/queryPlanEditor';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// Query Plan editor registration
const queryPlanEditorDescriptor = EditorPaneDescriptor.create(
QueryPlanEditor,
QueryPlanEditor.ID,
QueryPlanEditor.LABEL
);
Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane)
.registerEditorPane(queryPlanEditorDescriptor, [new SyncDescriptor(QueryPlanInput)]);
export class QueryPlanEditorOverrideContribution extends Disposable implements IWorkbenchContribution {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IEditorResolverService private _editorResolverService: IEditorResolverService
) {
super();
this.registerEditorOverride();
}
private registerEditorOverride(): void {
this._editorResolverService.registerEditor(
'', //Removing sqlplan glob pattern. TODO: to be removed entirely from ADS.
{
id: QueryPlanEditor.ID,
label: QueryPlanEditor.LABEL,
priority: RegisteredEditorPriority.builtin
},
{},
(editorInput, group) => {
const queryPlanInput = this._instantiationService.createInstance(QueryPlanInput, editorInput.resource);
return { editor: queryPlanInput, options: editorInput.options, group: group };
}
);
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(QueryPlanEditorOverrideContribution, LifecyclePhase.Restored);

View File

@@ -1,112 +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 * as QP from 'kburtram-query-plan';
import { IPanelView, IPanelTab } from 'sql/base/browser/ui/panel/panel';
import { Dimension, clearNode } from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { dispose } from 'vs/base/common/lifecycle';
import { QueryPlanState } from 'sql/workbench/common/editor/query/queryPlanState';
export class QueryPlanTab implements IPanelTab {
public readonly title = localize('queryPlanTitle', "Query Plan");
public readonly identifier = 'QueryPlanTab';
public readonly view: QueryPlanView;
constructor() {
this.view = new QueryPlanView();
}
public dispose() {
dispose(this.view);
}
public clear() {
this.view.clear();
}
}
export class QueryPlanView implements IPanelView {
private qp?: QueryPlan;
private xml?: string;
private container = document.createElement('div');
private _state?: QueryPlanState;
public render(container: HTMLElement): void {
container.appendChild(this.container);
this.container.style.overflow = 'scroll';
if (!this.qp) {
this.qp = new QueryPlan(this.container);
if (this.xml) {
this.qp.xml = this.xml;
}
}
}
dispose() {
this.container.remove();
this.qp = undefined;
}
public layout(dimension: Dimension): void {
this.container.style.width = dimension.width + 'px';
this.container.style.height = dimension.height + 'px';
}
public clear() {
if (this.qp) {
this.qp.xml = undefined;
}
}
public showPlan(xml: string) {
if (this.qp) {
this.qp.xml = xml;
} else {
this.xml = xml;
}
if (this.state) {
this.state.xml = xml;
}
}
public setState(val: QueryPlanState) {
this._state = val;
if (this._state.xml) {
this.showPlan(this._state.xml);
}
}
public get state(): QueryPlanState | undefined {
return this._state;
}
}
export class QueryPlan {
private _xml?: string;
constructor(private container: HTMLElement) {
}
public set xml(xml: string | undefined) {
this._xml = xml;
clearNode(this.container);
if (this.xml) {
QP.showPlan(this.container, this.xml, {
jsTooltips: false
});
this.container.querySelectorAll('div.qp-tt').forEach(toolTip => {
toolTip.classList.add('monaco-editor');
toolTip.classList.add('monaco-editor-hover');
});
}
}
public get xml(): string | undefined {
return this._xml;
}
}

View File

@@ -1,73 +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 * as DOM from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { QueryPlanView } from 'sql/workbench/contrib/queryPlan/browser/queryPlan';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
export class QueryPlanEditor extends EditorPane {
public static ID: string = 'workbench.editor.queryplan';
public static LABEL: string = localize('queryPlanEditor', "Query Plan Editor");
private view = this._register(new QueryPlanView());
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService
) {
super(QueryPlanEditor.ID, telemetryService, themeService, storageService);
}
/**
* Called to create the editor in the parent element.
*/
public createEditor(parent: HTMLElement): void {
//Enable scrollbars when drawing area is larger than viewport
parent.style.overflow = 'auto';
//Set background of parent to white (same as .qp-root from src\sql\parts\grid\load\css\qp.css)
//This is because the bottom-most tooltips can extend past the drawing area, which causes the
//scrolling area to have gaps on the bottom and left. So if the colors aren't matched then
//these gaps show up as different colors and look bad.
//Another option would be to check the tooltip positions and reposition them if necessary
//during the load - but changing the background color was the simplest and least error prone
//(plus it's probable that we won't be using this control in the future anyways if development)
//continues on the Query plan feature
parent.style.background = '#fff';
this.view.render(parent);
}
/**
* Updates the internal variable keeping track of the editor's size, and re-calculates the sash position.
* To be called when the container of this editor changes size.
*/
public layout(dimension: DOM.Dimension): void {
this.view.layout(dimension);
}
public override async setInput(input: QueryPlanInput, options: IEditorOptions, context: IEditorOpenContext): Promise<void> {
if (this.input instanceof QueryPlanInput && this.input.matches(input)) {
return Promise.resolve(undefined);
}
await input.resolve();
await super.setInput(input, options, context, CancellationToken.None);
this.view.showPlan(input.planXml!);
}
public override dispose(): void {
super.dispose();
}
}

View File

@@ -1,128 +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 { Dimension } from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
import { Table } from 'sql/base/browser/ui/table/table';
import { PlanXmlParser } from 'sql/workbench/contrib/queryPlan/browser/planXmlParser';
import { IPanelView, IPanelTab } from 'sql/base/browser/ui/panel/panel';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachTableStyler } from 'sql/platform/theme/common/styler';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { TopOperationsState } from 'sql/workbench/common/editor/query/topOperationsState';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
const topOperationColumns: Array<Slick.Column<any>> = [
{ name: localize('topOperations.operation', "Operation"), field: 'operation', sortable: true, width: 300 },
{ name: localize('topOperations.object', "Object"), field: 'object', sortable: true },
{ name: localize('topOperations.estCost', "Est Cost"), field: 'estCost', sortable: true },
{ name: localize('topOperations.estSubtreeCost', "Est Subtree Cost"), field: 'estSubtreeCost', sortable: true },
{ name: localize('topOperations.actualRows', "Actual Rows"), field: 'actualRows', sortable: true },
{ name: localize('topOperations.estRows', "Est Rows"), field: 'estRows', sortable: true },
{ name: localize('topOperations.actualExecutions', "Actual Executions"), field: 'actualExecutions', sortable: true },
{ name: localize('topOperations.estCPUCost', "Est CPU Cost"), field: 'estCPUCost', sortable: true },
{ name: localize('topOperations.estIOCost', "Est IO Cost"), field: 'estIOCost', sortable: true },
{ name: localize('topOperations.parallel', "Parallel"), field: 'parallel', sortable: true },
{ name: localize('topOperations.actualRebinds', "Actual Rebinds"), field: 'actualRebinds', sortable: true },
{ name: localize('topOperations.estRebinds', "Est Rebinds"), field: 'estRebinds', sortable: true },
{ name: localize('topOperations.actualRewinds', "Actual Rewinds"), field: 'actualRewinds', sortable: true },
{ name: localize('topOperations.estRewinds', "Est Rewinds"), field: 'estRewinds', sortable: true },
{ name: localize('topOperations.partitioned', "Partitioned"), field: 'partitioned', sortable: true }
];
export class TopOperationsTab extends Disposable implements IPanelTab {
public readonly title = localize('topOperationsTitle', "Top Operations");
public readonly identifier = 'TopOperationsTab';
public readonly view: TopOperationsView;
constructor(@IInstantiationService instantiationService: IInstantiationService) {
super();
this.view = this._register(instantiationService.createInstance(TopOperationsView));
}
public clear() {
this.view.clear();
}
}
export class TopOperationsView extends Disposable implements IPanelView {
private _state?: TopOperationsState;
private table: Table<any>;
private container = document.createElement('div');
private dataView = new TableDataView();
constructor(
@IThemeService private themeService: IThemeService,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super();
this.table = new Table(this.container, {
columns: topOperationColumns,
dataProvider: this.dataView,
sorter: (args) => {
this.dataView.sort(args);
}
});
this._register(this.table);
this._register(attachTableStyler(this.table, this.themeService));
this._register(this.dataView.onRowCountChange(() => this.table.updateRowCount()));
}
public render(container: HTMLElement): void {
container.appendChild(this.container);
this.telemetryService.sendActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.ViewTopOperations);
}
public layout(dimension: Dimension): void {
this.table.layout(dimension);
}
public clear() {
this.dataView.clear();
}
public showPlan(xml: string) {
this.state!.xml = xml;
this.dataView.clear();
let parser = new PlanXmlParser(xml);
let operations = parser.topOperations;
let data = operations.map(i => {
return {
operation: i.title,
object: i.indexObject?.title,
estCost: i.estimatedOperatorCost,
estSubtreeCost: i.subtreeCost,
actualRows: i.runtimeInfo?.actualRows,
estRows: i.estimateRows,
actualExecutions: i.runtimeInfo?.actualExecutions,
estCPUCost: i.estimateCpu,
estIOCost: i.estimateIo,
parallel: i.parallel,
actualRebinds: '',
estRebinds: i.estimateRebinds,
actualRewinds: '',
estRewinds: i.estimateRewinds,
partitioned: i.partitioned
};
});
this.dataView.push(data);
}
public setState(val: TopOperationsState) {
this._state = val;
if (this._state.xml) {
this.showPlan(this._state.xml);
}
}
public get state(): TopOperationsState | undefined {
return this._state;
}
}

View File

@@ -1,56 +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 { URI } from 'vs/base/common/uri';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
export class QueryPlanInput extends EditorInput {
public static ID: string = 'workbench.editorinputs.queryplan';
public static SCHEMA: string = 'queryplan';
private _xml?: string;
constructor(
private _uri: URI,
@ITextFileService private readonly fileService: ITextFileService
) {
super();
}
override get typeId(): string {
return UntitledTextEditorInput.ID;
}
public override getName(): string {
return 'Query Plan';
}
public get planXml(): string | undefined {
return this._xml;
}
public getUri(): string {
return this._uri.toString();
}
public supportsSplitEditor(): boolean {
return false;
}
public override async resolve(refresh?: boolean): Promise<EditorModel | null> {
if (!this._xml) {
this._xml = (await this.fileService.read(this._uri, { acceptTextOnly: true })).value;
}
return null;
}
get resource(): URI | undefined {
return undefined;
}
}

View File

@@ -474,9 +474,6 @@ import 'sql/workbench/contrib/objectExplorer/common/serverGroup.contribution';
// edit data editor
import 'sql/workbench/contrib/editData/browser/editData.contribution';
// query plan editor
import 'sql/workbench/contrib/queryPlan/browser/queryPlan.contribution';
//acounts
import 'sql/workbench/contrib/accounts/browser/accounts.contribution';
import 'sql/workbench/contrib/accounts/browser/accountManagement.contribution';