mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-10 18:22:36 -05:00
Sending show plan graph to ADS on Result Set updated event (#1300)
* Sending showplan graph over json rpc in Result updated event Translating showplan graph into simple objects to be sent over JSON RPC * Revert "Sending showplan graph over json rpc in Result updated event" This reverts commit 2d63a625fd200d057bf6093e233f05dea440347c. * Added string for localization * Sending showplan graph over json rpc in Result updated event Translating showplan graph into simple objects to be sent over JSON RPC * Refactoring class * Removing test warning * Removing unused imports Adding copyright * Removing unused prop * removing formatted string out .strings file * Formatting files Adding Errors in show plan graph * Adding a separate event for execution plan * Now sending mulitple graphs when a batch has more than one query.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution plan graph object that is sent over JSON RPC
|
||||
/// </summary>
|
||||
public class ExecutionPlanGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// Root of the execution plan tree
|
||||
/// </summary>
|
||||
public ExecutionPlanNode Root { get; set; }
|
||||
/// <summary>
|
||||
/// Underlying query for the execution plan graph
|
||||
/// </summary>
|
||||
public string Query { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the node. This determines the icon that is displayed for it
|
||||
/// </summary>
|
||||
public string Type { get; set; }
|
||||
/// <summary>
|
||||
/// Cost associated with the node
|
||||
/// </summary>
|
||||
public double Cost { get; set; }
|
||||
/// <summary>
|
||||
/// Cost of the node subtree
|
||||
/// </summary>
|
||||
public double SubTreeCost { get; set; }
|
||||
/// <summary>
|
||||
/// Relative cost of the node compared to its siblings.
|
||||
/// </summary>
|
||||
public double RelativeCost { get; set; }
|
||||
/// <summary>
|
||||
/// Time take by the node operation in milliseconds
|
||||
/// </summary>
|
||||
public long? ElapsedTimeInMs { get; set; }
|
||||
/// <summary>
|
||||
/// Node properties to be shown in the tooltip
|
||||
/// </summary>
|
||||
public List<ExecutionPlanGraphElementProperties> Properties { get; set; }
|
||||
/// <summary>
|
||||
/// Display name for the node
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Description associated with the node.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// Subtext displayed under the node name
|
||||
/// </summary>
|
||||
public string[] Subtext { get; set; }
|
||||
public List<ExecutionPlanNode> Children { get; set; }
|
||||
public List<ExecutionPlanEdges> Edges { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanGraphElementProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the property
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Formatted value for the property
|
||||
/// </summary>
|
||||
public string FormattedValue { get; set; }
|
||||
/// <summary>
|
||||
/// Flag to show/hide props in tooltip
|
||||
/// </summary>
|
||||
public bool ShowInTooltip { get; set; }
|
||||
/// <summary>
|
||||
/// Display order of property
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Flag to indicate if the property has a longer value so that it will be shown at the bottom of the tooltip
|
||||
/// </summary>
|
||||
public bool IsLongString { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanEdges
|
||||
{
|
||||
/// <summary>
|
||||
/// Count of the rows returned by the subtree of the edge.
|
||||
/// </summary>
|
||||
public double RowCount { get; set; }
|
||||
/// <summary>
|
||||
/// Size of the rows returned by the subtree of the edge.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public double RowSize { get; set; }
|
||||
/// <summary>
|
||||
/// Edge properties to be shown in the tooltip.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public List<ExecutionPlanGraphElementProperties> Properties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
public static string Parenthesis(string text)
|
||||
{
|
||||
return string.Format("({0})", text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
|
||||
if (parentNode != null)
|
||||
{
|
||||
parentNode.Children.AddLast(node);
|
||||
parentNode.Children.Add(node);
|
||||
}
|
||||
|
||||
// Add node to the hashtable
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
@@ -29,12 +31,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
{
|
||||
this.ID = id;
|
||||
this.properties = new PropertyDescriptorCollection(new PropertyDescriptor[0]);
|
||||
this.children = new LinkedList<Node>();
|
||||
this.childrenEdges = new LinkedList<Edge>();
|
||||
this.children = new List<Node>();
|
||||
this.childrenEdges = new List<Edge>();
|
||||
this.LogicalOpUnlocName = null;
|
||||
this.PhysicalOpUnlocName = null;
|
||||
this.root = context.Graph.Root;
|
||||
if(this.root == null)
|
||||
if (this.root == null)
|
||||
{
|
||||
this.root = this;
|
||||
}
|
||||
@@ -55,6 +57,159 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Node display name
|
||||
/// </summary>
|
||||
public virtual string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.Operation == Operation.Unknown)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
// The display name can consist of two lines
|
||||
// The first line is the Physical name and the physical kind in parenthesis
|
||||
// The second line should contains either Object value or LogicalOp name.
|
||||
// The second line should not show the same content as the first line.
|
||||
|
||||
string firstLine = this["PhysicalOp"] as string;
|
||||
if (firstLine == null)
|
||||
{
|
||||
if (this.Operation == null)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
firstLine = this.Operation.DisplayName;
|
||||
}
|
||||
|
||||
// Check if the PhysicalOp is specialized to a specific kind
|
||||
string firstLineAppend = this["PhysicalOperationKind"] as string;
|
||||
if (firstLineAppend != null)
|
||||
{
|
||||
firstLine = String.Format(CultureInfo.CurrentCulture, "{0} {1}", firstLine, Constants.Parenthesis(firstLineAppend));
|
||||
}
|
||||
|
||||
|
||||
string secondLine;
|
||||
|
||||
object objectValue = this["Object"];
|
||||
if (objectValue != null)
|
||||
{
|
||||
secondLine = GetObjectNameForDisplay(objectValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondLine = this["LogicalOp"] as string;
|
||||
if (secondLine != null)
|
||||
{
|
||||
if (secondLine != firstLine)
|
||||
{
|
||||
// Enclose logical name in parenthesis.
|
||||
secondLine = Constants.Parenthesis(secondLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't show the second line if its value is the same as on the first line.
|
||||
secondLine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return secondLine == null || secondLine.Length == 0
|
||||
? firstLine
|
||||
: String.Format(CultureInfo.CurrentCulture, "{0}\n{1}", firstLine, secondLine);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Node description
|
||||
/// </summary>
|
||||
[DisplayOrder(2), DisplayNameDescription(SR.Keys.OperationDescriptionShort, SR.Keys.OperationDescription)]
|
||||
public string Description
|
||||
{
|
||||
get { return this.Operation.Description; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value that indicates Node parallelism.
|
||||
/// </summary>
|
||||
public bool IsParallel
|
||||
{
|
||||
get
|
||||
{
|
||||
object value = this["Parallel"];
|
||||
return value != null ? (bool)value : false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value that indicates whether the Node has warnings.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public bool HasWarnings
|
||||
{
|
||||
get
|
||||
{
|
||||
return this["Warnings"] != null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value that indicates whether the Node has critical warnings.
|
||||
/// </summary>
|
||||
private bool HasCriticalWarnings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this["Warnings"] != null)
|
||||
{
|
||||
ExpandableObjectWrapper wrapper = this["Warnings"] as ExpandableObjectWrapper;
|
||||
if (wrapper["NoJoinPredicate"] != null)
|
||||
{
|
||||
return (bool)wrapper["NoJoinPredicate"];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this showplan_xml has PDW cost.
|
||||
/// </summary>
|
||||
private bool HasPDWCost
|
||||
{
|
||||
get
|
||||
{
|
||||
return this["PDWAccumulativeCost"] != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cost associated with the Node.
|
||||
/// </summary>
|
||||
[ShowInToolTip, DisplayOrder(8), DisplayNameDescription(SR.Keys.EstimatedOperatorCost, SR.Keys.EstimatedOperatorCostDescription)]
|
||||
public string DisplayCost
|
||||
{
|
||||
get
|
||||
{
|
||||
double cost = this.RelativeCost * 100;
|
||||
if (this.HasPDWCost && cost <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return SR.OperatorDisplayCost(this.Cost, (int)Math.Round(cost));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cost associated with the current Node.
|
||||
/// </summary>
|
||||
@@ -95,6 +250,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
/// <summary>
|
||||
/// Gets the cost associated with the Node subtree.
|
||||
/// </summary>
|
||||
[ShowInToolTip, DisplayOrder(9), DisplayNameDescription(SR.Keys.EstimatedSubtreeCost, SR.Keys.EstimatedSubtreeCostDescription)]
|
||||
public double SubtreeCost
|
||||
{
|
||||
get
|
||||
@@ -116,6 +272,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max Children X Position.
|
||||
/// </summary>
|
||||
public int MaxChildrenXPosition;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operation information (localized name, description, image, etc)
|
||||
/// </summary>
|
||||
@@ -174,11 +336,20 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
/// <summary>
|
||||
/// Gets collection of node children.
|
||||
/// </summary>
|
||||
public LinkedList<Node> Children
|
||||
public List<Node> Children
|
||||
{
|
||||
get { return this.children; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets collection of node children.
|
||||
/// </summary>
|
||||
public List<Edge> Edges
|
||||
{
|
||||
get { return this.childrenEdges; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets current node parent.
|
||||
/// </summary>
|
||||
@@ -320,6 +491,26 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
return true;
|
||||
}
|
||||
|
||||
public long? ElapsedTimeInMs
|
||||
{
|
||||
get
|
||||
{
|
||||
long? time = null;
|
||||
var actualStatsWrapper = this["ActualTimeStatistics"] as ExpandableObjectWrapper;
|
||||
if (actualStatsWrapper != null)
|
||||
{
|
||||
var counters = actualStatsWrapper["ActualElapsedms"] as RunTimeCounters;
|
||||
if (counters != null)
|
||||
{
|
||||
var elapsedTime = counters.MaxCounter;
|
||||
long ticks = (long)elapsedTime * TimeSpan.TicksPerMillisecond;
|
||||
time = new DateTime(ticks).Millisecond;
|
||||
}
|
||||
}
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ENU name for Logical Operator
|
||||
/// </summary>
|
||||
@@ -334,6 +525,33 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
|
||||
#region Implementation details
|
||||
|
||||
/// <summary>
|
||||
/// Gets short object name for display.
|
||||
/// Since database and schema is not important and displaying table first is much useful,
|
||||
/// we are displaying object name in [Table].[Index] [Alias] format.
|
||||
/// </summary>
|
||||
/// <param name="objectProperty">Object property in the property bag</param>
|
||||
private string GetObjectNameForDisplay(object objectProperty)
|
||||
{
|
||||
string objectNameForDisplay = string.Empty;
|
||||
|
||||
Debug.Assert(objectProperty != null);
|
||||
if (objectProperty != null)
|
||||
{
|
||||
objectNameForDisplay = objectProperty.ToString();
|
||||
|
||||
ExpandableObjectWrapper objectWrapper = objectProperty as ExpandableObjectWrapper;
|
||||
Debug.Assert(objectWrapper != null);
|
||||
if (objectWrapper != null)
|
||||
{
|
||||
objectNameForDisplay = ObjectWrapperTypeConverter.MergeString(".", objectWrapper["Table"], objectWrapper["Index"]);
|
||||
objectNameForDisplay = ObjectWrapperTypeConverter.MergeString(" ", objectNameForDisplay, objectWrapper["Alias"]);
|
||||
}
|
||||
}
|
||||
|
||||
return objectNameForDisplay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// used to compare multiple string type PropertyValue in Object properties,
|
||||
/// for ex: Server, Database, Schema, Table, Index, etc...
|
||||
@@ -358,6 +576,130 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets lines of text displayed under the icon.
|
||||
/// </summary>
|
||||
/// <returns>Array of strings.</returns>
|
||||
public string[] GetDisplayLinesOfText()
|
||||
{
|
||||
string newDisplayNameLines = this.DisplayName;
|
||||
|
||||
// cost
|
||||
double cost = this.RelativeCost * 100;
|
||||
|
||||
if (!this.HasPDWCost || cost > 0)
|
||||
{
|
||||
string costText = SR.CostFormat((int)Math.Round(cost));
|
||||
newDisplayNameLines += '\n' + costText;
|
||||
}
|
||||
|
||||
|
||||
// elapsed time in miliseconds
|
||||
string elapsedTime = GetElapsedTimeDisplayString();
|
||||
if (!String.IsNullOrEmpty(elapsedTime))
|
||||
{
|
||||
newDisplayNameLines += '\n' + elapsedTime;
|
||||
}
|
||||
|
||||
// actual/estimated rows
|
||||
string rowStatistics = GetRowStatisticsDisplayString();
|
||||
if (!String.IsNullOrEmpty(rowStatistics))
|
||||
{
|
||||
newDisplayNameLines += '\n' + rowStatistics;
|
||||
}
|
||||
|
||||
return newDisplayNameLines.Split('\n');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide a string for the actual elapsed time if it is available
|
||||
/// </summary>
|
||||
/// <returns>formatted string of execution time</returns>
|
||||
public string GetElapsedTimeDisplayString()
|
||||
{
|
||||
string formattedTime = null;
|
||||
|
||||
var actualStatsWrapper = this["ActualTimeStatistics"] as ExpandableObjectWrapper;
|
||||
if (actualStatsWrapper != null)
|
||||
{
|
||||
var counters = actualStatsWrapper["ActualElapsedms"] as RunTimeCounters;
|
||||
if (counters != null)
|
||||
{
|
||||
var elapsedTime = counters.MaxCounter;
|
||||
long ticks = (long)elapsedTime * TimeSpan.TicksPerMillisecond;
|
||||
var time = new DateTime(ticks);
|
||||
if (ticks < 1000L * TimeSpan.TicksPerMillisecond * 60) // 60 seconds
|
||||
{
|
||||
formattedTime = time.ToString("s.fff") + "s";
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate the hours
|
||||
long hours = ticks / (1000L * TimeSpan.TicksPerMillisecond * 60 * 60); //1 hour
|
||||
formattedTime = hours.ToString() + time.ToString(":mm:ss");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide a string for the actual rows vs estimated rows if they are both available in the actual execution plan
|
||||
/// </summary>
|
||||
/// <returns>formatted string of actual rows vs estimated rows; or null if estimateRows or actualRows is null</returns>
|
||||
private string GetRowStatisticsDisplayString()
|
||||
{
|
||||
var actualRowsCounters = this[NodeBuilderConstants.ActualRows] as RunTimeCounters;
|
||||
ulong? actualRows = actualRowsCounters != null ? actualRowsCounters.TotalCounters : (ulong?)null;
|
||||
var estimateRows = this[NodeBuilderConstants.EstimateRows] as double?;
|
||||
var estimateExecutions = this[NodeBuilderConstants.EstimateExecutions] as double?;
|
||||
|
||||
if (estimateRows != null)
|
||||
{
|
||||
if (estimateExecutions != null)
|
||||
{
|
||||
estimateRows = estimateRows * estimateExecutions;
|
||||
}
|
||||
// we display estimate rows as integer so need round function
|
||||
estimateRows = Math.Round(estimateRows.Value);
|
||||
}
|
||||
|
||||
return GetRowStatisticsDisplayString(actualRows, estimateRows);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inner function to provide a string for the actual rows vs estimated rows if they are both available in the actual execution plan
|
||||
/// </summary>
|
||||
/// <param name="actualRows">actual rows</param>
|
||||
/// <param name="estimateRows">estimated rows</param>
|
||||
/// <returns>formatted string of actual rows vs estimated rows; or null if any of the arguments is null</returns>
|
||||
private string GetRowStatisticsDisplayString(ulong? actualRows, double? estimateRows)
|
||||
{
|
||||
if (!actualRows.HasValue || !estimateRows.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// estimateRows should always to be positive, I just change it to 1 just in case since we need to calculate the percentage
|
||||
estimateRows = estimateRows > 0 ? estimateRows : 1;
|
||||
|
||||
// get the difference in percentage
|
||||
var actualString = actualRows.Value.ToString();
|
||||
var estimateString = estimateRows.Value.ToString();
|
||||
int percent = 100;
|
||||
if (estimateRows > 0)
|
||||
{
|
||||
percent = (int)(100 * ((double)actualRows / estimateRows));
|
||||
}
|
||||
|
||||
actualString = actualString.PadLeft(estimateString.Length);
|
||||
estimateString = estimateString.PadLeft(actualString.Length);
|
||||
|
||||
return SR.ActualOfEstimated(actualString, estimateString, percent);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
@@ -367,13 +709,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
private double subtreeCost;
|
||||
private Operation operation;
|
||||
private PropertyDescriptorCollection properties;
|
||||
private LinkedList<Node> children;
|
||||
private List<Node> children;
|
||||
private readonly string objectProperty = NodeBuilderConstants.Object;
|
||||
private readonly string predicateProperty = NodeBuilderConstants.LogicalOp;
|
||||
private Node parent;
|
||||
private Graph graph;
|
||||
private Edge parentEdge;
|
||||
private LinkedList<Edge> childrenEdges;
|
||||
private List<Edge> childrenEdges;
|
||||
private string nodeType;
|
||||
|
||||
private Node root;
|
||||
@@ -388,9 +730,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
public void AddChild(Node child)
|
||||
{
|
||||
Edge edge = new Edge(this, child);
|
||||
this.childrenEdges.AddLast(edge);
|
||||
this.childrenEdges.Add(edge);
|
||||
child.parentEdge = edge;
|
||||
this.children.AddLast(child);
|
||||
this.children.Add(child);
|
||||
child.parent = this;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph
|
||||
set { this.propertyValue = value; }
|
||||
}
|
||||
|
||||
public string DisplayValue
|
||||
{
|
||||
get => this.Converter.ConvertToString(null, null, this.Value);
|
||||
}
|
||||
|
||||
public int DisplayOrder
|
||||
{
|
||||
get
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan
|
||||
{
|
||||
public class ShowPlanGraphUtils
|
||||
{
|
||||
public static List<ExecutionPlanGraph> CreateShowPlanGraph(string xml)
|
||||
{
|
||||
ShowPlanGraph.ShowPlanGraph[] graphs = ShowPlanGraph.ShowPlanGraph.ParseShowPlanXML(xml, ShowPlanGraph.ShowPlanType.Unknown);
|
||||
return graphs.Select(g => new ExecutionPlanGraph
|
||||
{
|
||||
Root = ConvertShowPlanTreeToExecutionPlanTree(g.Root),
|
||||
Query = g.Statement
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static ExecutionPlanNode ConvertShowPlanTreeToExecutionPlanTree(Node currentNode)
|
||||
{
|
||||
return new ExecutionPlanNode
|
||||
{
|
||||
Type = currentNode.Operation.Image,
|
||||
Cost = currentNode.Cost,
|
||||
SubTreeCost = currentNode.SubtreeCost,
|
||||
Description = currentNode.Description,
|
||||
Subtext = currentNode.GetDisplayLinesOfText(),
|
||||
RelativeCost = currentNode.RelativeCost,
|
||||
Properties = GetProperties(currentNode.Properties),
|
||||
Children = currentNode.Children.Select(x => ConvertShowPlanTreeToExecutionPlanTree(x)).ToList(),
|
||||
Edges = currentNode.Edges.Select(x => ConvertShowPlanEdgeToExecutionPlanEdge(x)).ToList(),
|
||||
Name = currentNode.DisplayName,
|
||||
ElapsedTimeInMs = currentNode.ElapsedTimeInMs
|
||||
};
|
||||
}
|
||||
|
||||
private static ExecutionPlanEdges ConvertShowPlanEdgeToExecutionPlanEdge(Edge edge)
|
||||
{
|
||||
return new ExecutionPlanEdges
|
||||
{
|
||||
RowCount = edge.RowCount,
|
||||
RowSize = edge.RowSize,
|
||||
Properties = GetProperties(edge.Properties)
|
||||
};
|
||||
}
|
||||
|
||||
private static List<ExecutionPlanGraphElementProperties> GetProperties(PropertyDescriptorCollection props)
|
||||
{
|
||||
List<ExecutionPlanGraphElementProperties> propsList = new List<ExecutionPlanGraphElementProperties>();
|
||||
foreach (PropertyValue prop in props)
|
||||
{
|
||||
propsList.Add(new ExecutionPlanGraphElementProperties()
|
||||
{
|
||||
Name = prop.DisplayName,
|
||||
FormattedValue = prop.DisplayValue,
|
||||
ShowInTooltip = prop.IsBrowsable,
|
||||
DisplayOrder = prop.DisplayOrder,
|
||||
IsLongString = prop.IsLongString
|
||||
});
|
||||
}
|
||||
return propsList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.DataCollection.Common;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.ShowPlan.ShowPlanGraph;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ShowPlan
|
||||
|
||||
Reference in New Issue
Block a user